package helper.chart

import helper.base.debug.DebugHelper
import helper.base.debug.DebugHelper.DebuggerList
import helper.base.debug.DebugHelper.DebuggerList.Companion.makeDebuggerList
import helper.base.debug.DebugRes.d_pln_drawLineAndHistoryMinLineChart
import helper.base.list.ListToHelper.toHistoryMinList
import helper.base.list.ListToHelper.toXYData
import helper.chart.ChartHelper.JFreeChartEx.taskDisplayInFrame
import helper.chart.ChartHelper.JFreeChartEx.taskSaveToFile
import org.jfree.chart.ChartFactory
import org.jfree.chart.ChartPanel
import org.jfree.chart.ChartUtils
import org.jfree.chart.JFreeChart
import org.jfree.chart.annotations.XYTextAnnotation
import org.jfree.chart.plot.PlotOrientation
import org.jfree.chart.ui.TextAnchor
import org.jfree.data.Range
import org.jfree.data.xy.DefaultXYDataset
import java.awt.Color
import java.awt.Font
import java.io.File
import javax.swing.JFrame

object ChartHelper {
    object JFreeChartEx {
        fun JFreeChart.taskSaveToFile(
            fileName: String,
            width: Int = 500,
            height: Int = 400,
        ) {
            val outputFile = File(fileName)
            outputFile.parentFile.mkdirs()
            ChartUtils.saveChartAsPNG(outputFile, this, width, height)
        }

        fun JFreeChart.taskDisplayInFrame(
            frameTitle: String,
            width: Int = 500,
            height: Int = 400,
        ) {
            val chartPanel = ChartPanel(this)
            chartPanel.preferredSize = java.awt.Dimension(width, height)  // 调整尺寸
            JFrame(frameTitle).apply {
                contentPane = chartPanel
                pack()
                isVisible = true
            }
        }
    }

    object LineChartHelper {
        fun getLinesChart(
            yDataList: List<Pair<String, List<Double>>>,
            ifShowYDouble: Boolean = false,
        ): JFreeChart {
            val dataset = DefaultXYDataset()

            yDataList.forEachIndexed { index, (sKey, yData) ->
                dataset.addSeries(
                    sKey,
                    yData.toXYData(),
                )
            }

            val chart = ChartFactory.createXYLineChart(
                "", // 图表标题
                "Number of iterations",  // X轴标签
                "Expected cost",  // Y轴标签
                dataset,
                PlotOrientation.VERTICAL,
                true,// 显示图例
                true,
                false,
            )

            val plot = chart.xyPlot

            // 设置背景颜色为白色
            plot.backgroundPaint = Color.WHITE

            // 设置网格线颜色为浅灰色
            plot.domainGridlinePaint = Color.LIGHT_GRAY
            plot.rangeGridlinePaint = Color.LIGHT_GRAY

            // 自动检测纵坐标范围
            val allYValues = yDataList.flatMap { it.second }
            val minY = allYValues.minOrNull() ?: 0.0
            val maxY = allYValues.maxOrNull() ?: 0.0
            val minus = maxY - minY
            val minus2 = minus / 3
            plot.rangeAxis.range = Range(minY - minus2 - 1, maxY + minus2 + 1)

            if (ifShowYDouble) {
                val plot = chart.xyPlot
                yDataList.forEachIndexed { listIndex, (_, yData) ->
                    yData.forEachIndexed { index, value ->
                        XYTextAnnotation(
                            value.toString(),
                            index.toDouble(),
                            value,
                        ).apply {
                            font = Font(
                                "SansSerif",
                                Font.PLAIN,
                                10,
                            )
                            textAnchor = TextAnchor.TOP_CENTER
                            plot.addAnnotation(this)
                        }
                    }
                }
            }

            return chart
        }

        fun getLinesChart2(
            yDataList: List<List<Double>>,
            ifShowYDouble: Boolean = false,
        ): JFreeChart {
            return getLinesChart(
                yDataList.withIndex().map { (k, v) ->
                    Pair("Series ${k + 1}", v)
                }, ifShowYDouble
            )
        }

        fun drawLinesChart(
            yDataList: List<List<Double>>,
            ifShowYDouble: Boolean = false,
            savedFileName: String? = null,
            showedFrameTitle: String? = null,
            frameWidth: Int = 500,
            frameHeight: Int = 400,
        ) {
            val chart: JFreeChart = getLinesChart2(
                yDataList = yDataList,
                ifShowYDouble = ifShowYDouble,
            )
            savedFileName?.let {
                chart.taskSaveToFile(
                    it,
                    frameWidth,
                    frameHeight,
                )
            }
            showedFrameTitle?.let {
                chart.taskDisplayInFrame(
                    showedFrameTitle,
                    frameWidth,
                    frameHeight,
                )
            }
        }

        fun drawLineChart(
            yData: List<Double>,
            ifShowYDouble: Boolean = false,
            savedFileName: String? = null,
            showedFrameTitle: String? = null,
            frameWidth: Int = 500,
            frameHeight: Int = 400,
        ) {
            drawLinesChart(
                yDataList = listOf(yData), // 将单条曲线数据放在一个列表里
                ifShowYDouble = ifShowYDouble,
                savedFileName = savedFileName,
                showedFrameTitle = showedFrameTitle,
                frameWidth = frameWidth,
                frameHeight = frameHeight
            )
        }

        fun drawLineAndHistoryMinLineChart(
            yData: List<Double>,
            ifShowYDouble: Boolean = false,
            savedFileName: String? = null,
            showedFrameTitle: String? = null,
            frameWidth: Int = 500,
            frameHeight: Int = 400,
            debuggerList: DebuggerList = makeDebuggerList(),
        ) {
            debuggerList.add(d_pln_drawLineAndHistoryMinLineChart)
            debuggerList.run {
                d(
                    d_pln_drawLineAndHistoryMinLineChart,
                ){
                    println("drawLineAndHistoryMinLineChart:")
                    println("yData=${yData}")
                }
            }

            val chart: JFreeChart = getLinesChart(
                yDataList = listOf(
                    Pair("Historical optimum", yData.toHistoryMinList()),
                    Pair("Current value", yData),
                ),
                ifShowYDouble = ifShowYDouble,
            )
            savedFileName?.let {
                chart.taskSaveToFile(
                    it,
                    frameWidth,
                    frameHeight,
                )
            }
            showedFrameTitle?.let {
                chart.taskDisplayInFrame(
                    showedFrameTitle,
                    frameWidth,
                    frameHeight,
                )
            }
        }
    }
}